#include <sstream>

#include "Robot.h"
#include "etats/EtatRobot.h"
#include "etats/EtatAVide.h"
#include "etats/EtatAVideFaceObstacle.h"
#include "etats/EtatEnCharge.h"
#include "etats/EtatEnChargeFaceObstacle.h"
#include "etats/EtatFige.h"

Robot::Robot() {
	_obstacle = NULL;
	_position = new Position();
	_objet = NULL;
	_direction = "Nord";
	_etatRobot = EtatAVide::getEtat();
	_etatCourant = "EtatAVide";
}

void Robot::avancer(int x, int y) {
	try {
		_etatRobot->avancer();
		_position->setX(x);
		_position->setY(y);
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas avancer.");
		throw EtatRobot::BadAction();
	}
}

void Robot::tourner(string dir) {
	try {
		_etatRobot->tourner(this);
		_direction = dir;
		if (_obstacle != NULL) {
			_obstacle = NULL;
		}
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas tourner.");
		throw EtatRobot::BadAction();
	}

}

void Robot::saisir(Objet* obj) {
	try {
		_etatRobot->saisir(this);
		_objet = obj;
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas saisir d'objet.");
		throw EtatRobot::BadAction();
	}
}

int Robot::peser() {
	try {
		_etatRobot->peser();
		string p = "Poids de l'objet : ";
		ostringstream oss;
		oss << getObjet()->getPoids();
		p += oss.str();
		notify(p);
		notify();
		return getObjet()->getPoids();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas peser d'objet.");
		throw EtatRobot::BadAction();
	}
	return -1;
}

void Robot::rencontrerObstacle(Obstacle* obs) {
	try {
		_etatRobot->rencontrerObstacle(this);
		_obstacle = obs;
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot est déjà face à un obstacle, ou le robot est figé.");
		throw EtatRobot::BadAction();
	}
}

int Robot::evaluerObstacle() {
	try {
		_etatRobot->evaluerObstacle();
		notify();
		return getObstacle()->getHauteur();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas évaluer d'obstacle.");
		throw EtatRobot::BadAction();
	}
	return -1;
}

void Robot::figer() {
	try {
		_etatRobot->figer(this);
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot est déjà figé.");
		throw EtatRobot::BadAction();
	}
}

// methode qui permet de faire repartir un robot fige
void Robot::repartir() {
	try {
		_etatRobot->repartir(this);
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot n'est pas figé.");
		throw EtatRobot::BadAction();
	}
}

void Robot::poser() {

	try {
		_etatRobot->poser(this);
		_objet = NULL;
		notify();
	} catch (EtatRobot::BadAction) {
		notify("le robot ne peut pas poser d'objet");
		throw EtatRobot::BadAction();
	}
}

void Robot::setEtat(EtatRobot* etat) {
	this->_etatRobot = etat;
}

EtatRobot* Robot::getEtat() {
	return _etatRobot;
}

string Robot::getEtatCourant() {
	return _etatCourant;
}

void Robot::setEtatCourant(string s) {
	_etatCourant = s;
}

Obstacle* Robot::getObstacle() {
	return _obstacle;
}

Objet* Robot::getObjet() {
	return _objet;
}

Position* Robot::getPosition() {
	return _position;
}

string Robot::getDirection() {
	return _direction;
}

void Robot::notify() {
	for (set<Afficheur*>::const_iterator it = _list_afficheurs.begin(); it
			!= _list_afficheurs.end(); it++)
		(*it)->afficher(this);
}

void Robot::notify(string message) {
	for (set<Afficheur*>::const_iterator it = _list_afficheurs.begin(); it
			!= _list_afficheurs.end(); it++)
		(*it)->afficher(message);
}

void Robot::attache(Afficheur* aff) {
	_list_afficheurs.insert(aff);
}

void Robot::detache(Afficheur* aff) {
	_list_afficheurs.erase(aff);
}
